/** @file    EncoderProtocolXmlL2.cpp
 *  @author  Alessandro Polo
 *  @version $Id: EncoderProtocolXmlL2.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the wosh::persistence::libxml2::EncoderProtocolXmlL2 class.
 * The header for this class can be found in EncoderProtocolXmlL2.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/persistence/libxml2/EncoderProtocolXmlL2.h>
 
// #include <framework/persistence/libxml2/PersistenceDbXmlL2.h>

 #include <core/ObjectFactory.h>
 #include <core/Utilities.h>
 #include <core/SystemError.h>
 #include <core/FileSystem.h>
 #include <core/DataModels.h>
 #include <core/MethodsCommon.h>


namespace wosh {
 namespace persistence {
  namespace libxml2 {


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::persistence::libxml2::EncoderProtocolXmlL2, "wosh::persistence::EncoderProtocolXml", 1.01, _static_EncoderProtocolXmlL2 )

EncoderProtocolXmlL2::EncoderProtocolXmlL2()
	: EncoderProtocolXml() {
	Object::setName("EncoderProtocolXmlL2", Utilities::randomLong() );
 }

EncoderProtocolXmlL2::~EncoderProtocolXmlL2() {
	xmlCleanupParser();
 }

PersistenceDb* EncoderProtocolXmlL2::createDb() const {
	return NULL;
 }
/*
EncoderBase* EncoderProtocolXmlL2::getEncoderFor_( const char* classname, double version, bool recursive ) {
	ObjectTypeInfo info(classname, "", version, true);
	if ( getEncoders().exists(info) )
		return getEncoders().find(info);
	if ( !recursive )
		return NULL;
	std::vector<ObjectTypeInfo> class_list;
	WRESULT ret = ObjectFactory::gatherInfoOf(class_list, classname, version);
	if ( WFAILED(ret) || class_list.empty() )
		return NULL;
	std::vector<ObjectTypeInfo>::const_iterator it;
	for (it=class_list.begin(); it!=class_list.end(); ++it) {
		if ( (*it).name == info.name ) continue;
		if ( getEncoders().exists(*it) )
			return getEncoders().find(*it);
		EncoderBase* enc = getEncoderFor_( (*it).name.c_str(), (*it).version, recursive );
		if ( enc != NULL )
			return enc;
	 }
	return NULL;
 }
*/
WRESULT EncoderProtocolXmlL2::saveObject( const ISerializable* object, const std::string& parameters ) {
	if ( object == NULL ) return WRET_ERR_PARAM;
	getEncoders().transactionBeginRead();

	EncoderBaseXmlL2* enc = dynamic_cast<EncoderBaseXmlL2*>( getEncoderFor_(object->getClassName()) );
	if ( enc == NULL ) {
		getEncoders().transactionEnd();
		return WRET_ERR_ILLEGAL_USE;
	 }
	WRESULT ret = WRET_ERR_INTERNAL;
	//enc->serializeTo(object, 
	getEncoders().transactionEnd();

	return ret;
 }

WRESULT EncoderProtocolXmlL2::saveObjects( const std::vector<ISerializable*>& objects, const std::string& parameters ) {
	WRESULT ret = WRET_ERR_INTERNAL;
	std::string filename = parameters;
	FilePath xmlFile;
	if ( !xmlFile.set( filename ) ) {
		Log(LOG_CRITICAL, ":saveObjects() FAILED resolving of %s", filename.c_str() );
		return WRET_ERR_PARAM;
	 }

	Log(LOG_VERBOSE, ":saveObjects() saving %d items in %s", objects.size(), xmlFile.getPathLocal().c_str() );

	xmlDocPtr doc = xmlNewDoc( BAD_CAST "1.0" );
	xmlNsPtr ns = NULL;
	xmlNodePtr rootNode = xmlNewNode( NULL, BAD_CAST "WOSH" );
	xmlDocSetRootElement(doc, rootNode);
	xmlNewProp( rootNode, BAD_CAST "encoder-class", BAD_CAST getClassName() );
	xmlNewProp( rootNode, BAD_CAST "encoder-protocol", BAD_CAST getProtocol() );
	xmlNewProp( rootNode, BAD_CAST "encoder-version", BAD_CAST Utilities::toString(getProtocolVersion()).c_str() );

	std::vector<std::string> classErrs;
	int j = 0;
	std::vector<ISerializable*>::const_iterator it;
	xmlNodePtr cNode = NULL;
	for ( it=objects.begin(); it != objects.end(); ++it, ++j ) {
		if ( *it == NULL ) continue;
		EncoderBaseXmlL2* enc = dynamic_cast<EncoderBaseXmlL2*>( getEncoderFor_((*it)->getClassName()) );
		if ( enc == NULL ) {
			classErrs.push_back((*it)->getClassName());
			Log(LOG_CRITICAL, ":saveObjects() Node#%d : Class not found '%s'", j, (*it)->getClassName() );
			continue;
		 }
		cNode = xmlNewChild(rootNode, NULL, BAD_CAST (*it)->getClassName(), NULL);
		WRESULT ret = enc->serializeTo( *it, doc, ns, cNode, false );
		if ( WFAILED(ret) )
			Log(LOG_WARNING, ":saveObjects() Node#%d : %s Error ret=%d", j, (*it)->getClassName(), ret );
		else
			Log(LOG_VERBOSE, ":saveObjects() Node#%d : saved.", j );
	 }

	xmlSaveFile( xmlFile.getPathLocal().c_str(), doc);
	xmlFreeDoc(doc);

	//wosh::SystemError::raise(this, Log, SystemError::CRITICAL, 0, "saveObjects", "FAILED#1 : Cannot write to file %s", xmlFile.getPathLocal().c_str() );

	if ( !classErrs.empty() ) {
		std::string errs = wosh::Utilities::joinVector(classErrs, "; ");
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "saveObjects", "%d ERRORS: Classes not found: %s", classErrs.size(), errs.c_str() );
	 }

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT EncoderProtocolXmlL2::loadObjects( std::vector<ISerializable*>& objects, const std::string& xml_source )
 {
	FilePath xmlFile;
	if ( !xmlFile.set( xml_source ) ) {
		Log(LOG_CRITICAL, ":loadObjects() FAILED resolving of %s", xml_source.c_str() );
		return WRET_ERR_PARAM;
	 }
	std::vector<std::string> sourceFiles;
	if ( xmlFile.isFolder() ) {
		WRESULT ret = FileSystem::getFilesOf( xmlFile.getPathLocal(), sourceFiles, true, true, ".xml" );
		if ( WFAILED(ret) || sourceFiles.empty() ) {
			Log(LOG_CRITICAL, ":loadObjects() FAILED getting files of %s", xmlFile.getPathLocal().c_str() );
			return WRET_ERR_PARAM;
		 }
		Log(LOG_VERBOSE, ":loadObjects() parsing %d files of folder %s", sourceFiles.size(), xmlFile.getPathLocal().c_str() );
	 }
	else if ( xmlFile.isFile() ) {
		sourceFiles.push_back( xmlFile.getPathLocal() );
	 }
	else {
		Log(LOG_CRITICAL, ":loadObjects() FAILED file %s doesn't exist!", xmlFile.getPathLocal().c_str() );
		return WRET_ERR_PARAM;
	 }

	std::string filename;
	std::vector<std::string>::iterator it;
	for ( it=sourceFiles.begin(); it!=sourceFiles.end(); ++it ) {
		filename = *it;
		xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0);
		xmlNsPtr ns = NULL;
		if (doc == NULL) {
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "loadObjects", "FAILED#1 : Cannot load document %s", filename.c_str() );
			continue;
		 }
		std::vector<std::string> classErrs;
		std::string tagname;
		int j = 0;
		xmlNodePtr rootNode = xmlDocGetRootElement(doc);
		for ( xmlNodePtr cur_node = rootNode; cur_node != NULL; cur_node = cur_node->next ) {
			if ( cur_node == NULL || cur_node->type != XML_ELEMENT_NODE ) continue;
			++j;
			if ( xmlHasProp( cur_node, BAD_CAST "classname") )
				tagname = (const char*)xmlGetProp( cur_node, BAD_CAST "classname");
			else
				tagname = (const char*)cur_node->name;
			if ( tagname == "" ) {
				Log(LOG_WARNING, ":loadObjects() Node#%d : Empty TagName", j );
				continue;
			 }
			EncoderBaseXmlL2* enc = dynamic_cast<EncoderBaseXmlL2*>( getEncoderFor_(tagname.c_str()) );
			if ( enc == NULL ) {
				classErrs.push_back(tagname);
				Log(LOG_CRITICAL, ":loadObjects() Node#%d : FAILED Class not found '%s'", j, tagname.c_str() );
				continue;
			 }
			ISerializable* obj = ObjectFactory::createTypeOf<ISerializable>(tagname);

			WRESULT ret = enc->deserializeFrom(obj, doc, ns, cur_node);
			if ( WFAILED(ret) )
				Log(LOG_WARNING, ":loadObjects() Node#%d : %s Deserialization-Error ret=%d", j, tagname.c_str(), ret );
			else
				Log(LOG_VERBOSE, ":loadObjects() Node#%d : %s loaded.", j, tagname.c_str() );

			objects.push_back( obj );
		 }

		if ( !classErrs.empty() ) {
			std::string errs = Utilities::joinVector(classErrs, "; ");
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "loadObjects", "%d ERRORS: Classes not found: %s", classErrs.size(), errs.c_str() );
		 }

		xmlFreeDoc(doc);
	 }

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int64 EncoderProtocolXmlL2::serializeTo( const ISerializable* object, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr contNode, int encoder_options ) {
	if ( object == NULL ) return -1;
	getEncoders().transactionBeginRead();
	EncoderBaseXmlL2* enc = dynamic_cast<EncoderBaseXmlL2*>( getEncoderFor_(object->getClassName()) );
	if ( enc == NULL ) {
		getEncoders().transactionEnd();
		return -2;
	 }
	xmlAttrPtr newattr = xmlNewProp(contNode, (const xmlChar *)"classname", (const xmlChar *)object->getClassName());
	if ( !EncoderBase::isCompact(encoder_options) )
		xmlNewProp(contNode, (const xmlChar *)"encoder", (const xmlChar *)enc->getClassName());
	int64 ret = enc->serializeTo(object, doc, ns, contNode, encoder_options);
	getEncoders().transactionEnd();
	return ret;
 }

int64 EncoderProtocolXmlL2::deserializeFrom( ISerializable* object, xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr contNode ) {
	if ( object == NULL ) return -1;
	getEncoders().transactionBeginRead();
	EncoderBaseXmlL2* enc = dynamic_cast<EncoderBaseXmlL2*>( getEncoderFor_(object->getClassName()) );
	if ( enc == NULL ) {
		getEncoders().transactionEnd();
		return -2;
	 }
	int64 ret = enc->deserializeFrom(object, doc, ns, contNode);
	getEncoders().transactionEnd();
	return ret;
 }

ISerializable* EncoderProtocolXmlL2::deserializeFrom( xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr contNode, WRESULT* return_code ) {
	std::string classname = ""; double version = 0.0;
	xmlChar* cl = xmlGetProp(contNode, (const xmlChar *)"classname");
	if ( cl != NULL ) {
		classname.assign( (const char *)cl);
		xmlFree(cl);
	 }
	getEncoders().transactionBeginRead();
	EncoderBaseXmlL2* enc = dynamic_cast<EncoderBaseXmlL2*>( getEncoderFor_(classname.c_str()) );
	if ( enc == NULL ) {
		if ( return_code != NULL ) *return_code = WRET_ERR_PARAM;
		getEncoders().transactionEnd();
		return NULL;
	 }
	ISerializable* object = ObjectFactory::createTypeOf<ISerializable>(classname, version);
	if ( object == NULL ) {
		if ( return_code != NULL ) *return_code = WRET_ERR_ILLEGAL_USE;
		getEncoders().transactionEnd();
		return NULL;
	 }
	WRESULT ret = enc->deserializeFrom(object, doc, ns, contNode);
	getEncoders().transactionEnd();
	if ( return_code != NULL ) *return_code = ret;
	return object;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////


 WOSH_REGISTER_INTERFACE(wosh::persistence::libxml2::EncoderBaseXmlL2, "wosh::persistence::EncoderBaseXml", 1.0, _static_EncoderBaseXmlL2 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int64 EncoderBaseXmlL2::serializeTo( const ISerializable* object, char* output, int64 buffer_size, int encoder_options ) {
	return 0;
 }

int64 EncoderBaseXmlL2::deserializeFrom( ISerializable* object, const char* buffer, int64 buffer_size ) {

	return 0;
 }
/*
int64 EncoderBaseXmlL2::serializeTo( const ISerializable* object, const std::string& filename, int encoder_options ) {
	return 0;
 }

WRESULT EncoderBaseXmlL2::deserializeFrom( ISerializable* object, const std::string& filename ) {
	return WRET_OK;
 }
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void EncoderBaseXmlL2::serializeString( const std::string& field, const std::string& value, xmlNodePtr contNode, int encoder_options ) {
	if ( isCompact(encoder_options) ) {
		if ( value.empty() ) return;
		xmlSetProp( contNode, BAD_CAST field.c_str(), BAD_CAST value.c_str() );
	 }
	else {
		xmlNodePtr newNode = xmlNewChild(contNode, NULL, BAD_CAST field.c_str(), NULL);
		xmlNodeSetContent(newNode, BAD_CAST value.c_str() );
	 }
 }

std::string EncoderBaseXmlL2::deserializeString( const std::string& field, const std::string& default_value, xmlNodePtr contNode, bool* valid ) {
	if ( xmlHasProp( contNode, BAD_CAST field.c_str()) )
		return std::string( (const char*)xmlGetProp( contNode, BAD_CAST field.c_str() ) );
	for ( xmlNodePtr cur_node = contNode; cur_node != NULL; cur_node = cur_node->next ) {
		if ( cur_node == NULL || cur_node->type != XML_ELEMENT_NODE || strcmp((const char*) cur_node->name, field.c_str()) != 0 ) continue;
		xmlChar* data = xmlNodeGetContent(cur_node);
		if ( data != NULL )
			return std::string( (const char*) data );
	 }
	return default_value;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

  }; // namespace libxml2
 }; // namespace persistence
}; // namespace wosh
